#!/bin/env python3
# -*- encoding=utf-8 -*-
#******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: yaokai
# Create: 2020-10-26
# ******************************************************************************
"""
sync code from gitee to obs
"""
import sys
import os
import time
import subprocess
from common.log_obs import log
from common.common import Pexpect
from common.parser_config import ParserConfigIni


def run(cmd, timeout=600, print_out=False):
    """
    run shell cmd
    :param cmd: command
    :param timeout: timeout
    :param print_out: print out or not
    :return: return code, stdout, stderr
    """
    ret = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8",
                         timeout=timeout)
    log.info("cmd: {}".format(cmd))
    if ret.stdout and print_out:
        log.info("ret.stdout: {}".format(ret.stdout))
    if ret.stderr:
        log.warning("ret.stderr: {}".format(ret.stderr))
    return ret.returncode, ret.stdout, ret.stderr


class SYNCCode(object):
    """
    if the rpm package has changed in gitee what you should donext
    (Synchronize the latest code to OBS)
    """

    def __init__(self, **kwargs):
        """
        init
        :param kwargs: dict, key_value as the following:
            repository: The package you want sync it to obs
            gitee_branch: The branch for you commit in gitee
            giteeuser: The gitee account
            giteeuserpwd: The gitee passwd
            meta_path: Where the obs_meta save in jenkins
            pkgs: packages
            project: obs project
            sync_type: sync type, like broken, All, package
            obs_server_user: The user name for you use in server
            obs_server_ip: The ip for you server
            obs_server_passwd: The password for your ip
            obs_server_port: The port for your ip
        """
        self.timestr = time.strftime("%Y%m%d %H-%M-%S", time.localtime())
        self.kwargs = kwargs
        self.repository = self.kwargs['repository']
        self.gitee_branch = self.kwargs['branch']
        self.giteeuser = self.kwargs['gitee_user']
        self.giteeuserpwd = self.kwargs['gitee_pwd']
        self.meta_path = self.kwargs['obs_meta_path']
        self.pkgs = self.kwargs['pkglist']
        self.project = self.kwargs['project']
        self.sync_type = self.kwargs['sync_type']
        self.cmd = Pexpect(self.kwargs['source_server_user'],
                self.kwargs['source_server_ip'],
                self.kwargs['source_server_pwd'],
                self.kwargs['source_server_port'])
        par = ParserConfigIni()
        self.obs_pkg_rpms_url = par.get_repos_dict()["obs_pkg_rpms"]
        self.obs_pkg_prms_files_dir = None
        self.sync_failed_rpms = []
        self.obs_ignored_package = par.get_obs_ignored_package()
        self.obs_include_project = par.get_obs_include_project()

    def _git_clone(self, rpm_dir, gitee_branch, path):
        """
        git clone
        :param rpm_dir: The name for git repository "it will exist under the current directory
        :param gitee_branch: The branch you want to checkout for this git repository
        :param path: repository path
        :return: bool
        """
        log.info("Git clone the %s in %s" % (rpm_dir, gitee_branch))
        self.cmd.ssh_cmd("rm -rf %s " % (path))
        repo_url = "https://%s:%s@gitee.com/src-openEuler/%s" % (self.giteeuser, self.giteeuserpwd, rpm_dir)
        for i in range(5):
            self.cmd.ssh_cmd("git lfs clone --depth=1 %s -b %s %s" % (repo_url, gitee_branch, path), 600)
            pull_result_last = str(self.cmd.ssh_cmd('git -C %s pull' % path)[1].strip()).split("'")[1]
            if "Already" in pull_result_last:
                log.info(pull_result_last)
                log.info("At now %s the branch is in %s" % (rpm_dir, gitee_branch))
                return True
            else:
                log.info("_GIT_CLONE: %s" % i)
                self.cmd.ssh_cmd("rm -rf %s" % path)
        log.error("_GIT_CLONE_ERROR: %s" % rpm_dir)
        return False

    def _get_obs_project(self):
        """
        Get the obs project from gitee_branch
        :return: obs project
        """
        log.info("Start get the obs_project")
        if "Multi-Version" in self.gitee_branch:
            path = self.meta_path + '/' + 'multi_version/' + self.gitee_branch
            log.info(path)
            cmd = "find %s -name %s | awk -F '/' '{print $5}'" % (path, self.repository)
        else:
            path = self.meta_path + '/' + self.gitee_branch
            log.info(path)
            cmd = "find %s -name %s | awk -F '/' '{print $4}'" % (path, self.repository)
        _, all_project, _ = run(cmd, print_out=True)
        obs_project = None
        for project in all_project.splitlines():
            obs_project = project.strip()
            if ":Bak" in obs_project:
                continue
            else:
                break
        if obs_project and ":Bak" not in obs_project:
            log.info("The %s obs_project for gitee_%s is <<<<%s>>>>" % (
                self.repository, self.gitee_branch, obs_project))
            return obs_project
        else:
            log.error("Failed !!! The rpm is not exist in %s branch !!!" % self.gitee_branch)
            return ""

    def _get_latest_gitee_pull(self):
        """
        make the obs server code be the latest
        :return: bool
        """
        if self.gitee_branch == "master":
            source_path = "/srv/cache/obs/tar_scm/repo/next/openEuler"
        else:
            source_path = "/srv/cache/obs/tar_scm/repo/next/" + self.gitee_branch
        if self.repository == "kernel":
            tag_path = "%s/kernel/SOURCE" % source_path
            ssh_cmd = "if [ -d %s ];then echo 'exist';else echo 'need to clone';fi" % tag_path
            repository_exist = self.cmd.ssh_cmd(ssh_cmd)
            repository_exist = str(repository_exist[1].strip()).split("'")[1]
            if repository_exist != "exist":
                self._git_clone(self.repository, self.gitee_branch, f"{source_path}/kernel")
            self.cmd.ssh_cmd("cd %s/kernel;git checkout %s" % (source_path, self.gitee_branch))
            pull_result = str(self.cmd.ssh_cmd("git -C %s/kernel pull" % source_path)[1].strip()).split("'")[1]
            log.info(pull_result)
            kernel_tags = str(self.cmd.ssh_cmd("cat %s" % tag_path)[1].strip()).split("'")[1]
            log.info(kernel_tags)
            open_kernel_path = "%s/openEuler-kernel/kernel" % source_path
            open_kernel_git = "https://%s:%s@gitee.com/openeuler/kernel" % (self.giteeuser, self.giteeuserpwd)
            log.info(open_kernel_git)
            self.cmd.ssh_cmd("rm -rf %s" % open_kernel_path)
            for i in range(5):
                self.cmd.ssh_cmd("git lfs clone --depth=1 %s -b %s %s" % (open_kernel_git,
                    kernel_tags, open_kernel_path), 600)
                pull_result_last = str(self.cmd.ssh_cmd('git -C %s pull' % open_kernel_path)[1].strip()).split("'")[1]
                if "Already" in pull_result_last:
                    log.info(pull_result_last)
                    log.info("kernel gitee pull success")
                    return True
                else:
                    self.cmd.ssh_cmd("rm -rf %s" % open_kernel_path)
            log.error("kernel gitee pull failed")
            return False
        else:
            rpm_path = source_path + '/' + self.repository
            ssh_cmd = "if [ -d %s ];then echo 'exist';else echo 'need to clone';fi" % rpm_path
            repository_exist = self.cmd.ssh_cmd(ssh_cmd)
            repository_exist = str(repository_exist[1].strip()).split("'")[1]
            log.info(self.repository + ':' + repository_exist)
            if repository_exist == 'exist':
                self.cmd.ssh_cmd("cd %s;git checkout %s" % (rpm_path, self.gitee_branch))
                pull_result = str(self.cmd.ssh_cmd('git -C %s pull' % rpm_path)[1].strip()).split("'")[1]
                log.info(pull_result)
                pull_result_last = str(self.cmd.ssh_cmd('git -C %s pull' % rpm_path)[1].strip()).split("'")[1]
                if "Already" in pull_result_last:
                    log.info(pull_result_last)
                    return True
                else:
                    clone_result = self._git_clone(self.repository, self.gitee_branch, rpm_path)
                    return clone_result
            else:
                clone_result = self._git_clone(self.repository, self.gitee_branch, rpm_path)
                return clone_result

    def _gitee_pr_to_obs(self, obs_pro):
        """
        obs trigger service
        :param obs_pro: The obs project that gitee branch corresponds
        :return: bool
        """
        cmd = "osc service remoterun %s %s" % (obs_pro, self.repository)
        _, osc_service, _ = run(cmd, print_out=True)
        for results in osc_service.splitlines():
            if "ok" in results:
                log.info("Success for osc service remoterun the %s" % self.repository)
                return True
            else:
                continue
        log.error("Failed !!! fail to service remoterun !!!")
        return False

    def _pre_sync_code(self, project=None):
        """
        The way to offer that make all fuction runing
        :param project: obs project
        :return: bool
        """
        if not project:
            obs_project = self._get_obs_project()
            if not obs_project:
                log.error("SYNC get obs project for %s branch %s failed" % (self.repository, self.gitee_branch))
                return False
        else:
            obs_project = project
            log.info("The %s in %s" % (self.repository, obs_project))
        if obs_project not in self.obs_include_project:
            pull_result = self._get_latest_gitee_pull()
            remoterun_result = self._gitee_pr_to_obs(obs_project)
            if obs_project and pull_result and remoterun_result:
                log.info("SYNC %s in %s SUCCESS" % (self.repository, obs_project))
                return True
            else:
                log.error("SYNC %s in %s ERROR: Please check the log.error" % (self.repository, obs_project))
                return False
        else:
            if self.repository in self.obs_ignored_package:
                pull_result = self._get_latest_gitee_pull()
                remoterun_result = self._gitee_pr_to_obs(obs_project)
                if obs_project and pull_result and remoterun_result:
                    log.info("SYNC %s in %s SUCCESS" % (self.repository, obs_project))
                    return True
                else:
                    log.error("SYNC %s in %s ERROR: Please check the log.error" % (self.repository, obs_project))
                    return False
            else:
                remoterun_result = self._gitee_pr_to_obs(obs_project)
                if obs_project and remoterun_result:
                    log.info("SYNC MASTER %s in %s SUCCESS" % (self.repository, obs_project))
                    return True
                else:
                    log.error("SYNC %s in %s ERROR: Please check the log.error" % (self.repository, obs_project))
                    return False

    def sync_code_to_obs(self):
        """
        The only way to offer that make all fuction runing
        :return:
        """
        if not self.pkgs:
            if self.repository and self.gitee_branch:
                if not self._pre_sync_code(self.project):
                    raise SystemExit("SYNC %s ERROR" % self.repository)
            else:
                raise SystemExit('please check you arguments')
        else:
            if self.sync_type == "broken":
                broken_cmd = "osc r --csv %s -r standard_aarch64 -a aarch64 2>/dev/null | \
                        grep broken | awk -F ';' '{print $1}'" % self.project
                log.info("Get the broken rpm:%s" % broken_cmd)
                _, broken_result, _ = run(broken_cmd)
                broken_list = broken_result.splitlines()
                log.debug(broken_result)
                self.pkgs = [x for x in broken_list if x != '']
                log.info("broken_rpmlist: %s" % self.pkgs)
                if not self.pkgs:
                    log.info("There are no broken pkgs in %s" % self.project)
            if self.sync_type == "All":
                if not self.repository and self.project:
                    cmd = "osc ls %s 2>/dev/null" % self.project
                    _, pkgs, _ = run(cmd)
                    pkg_list = pkgs.splitlines()
                    self.pkgs = [x for x in pkg_list if x != '']
                    log.info("project_rpmlist: %s" % self.pkgs)
                if not self.pkgs:
                    log.info("There are no pkgs in %s" % self.project)
            for pkg in self.pkgs:
                self.repository = pkg.strip()
                sync_result = self._pre_sync_code(self.project)
                if not sync_result:
                    self.sync_failed_rpms.append(self.repository)
            if self.sync_failed_rpms:
                log.error("SYNC ERROR LIST: %s" % ",".join(self.sync_failed_rpms))
                raise SystemExit("Failed, There are some pkgs sync failure")


